/**
 Team: Dream-Lab
 Author: مۇختەرجان مەخمۇت (yeganaaa@163.com)
 Time: 2020/10/9 4:51 PM GMT+06:00
**/
import {BridgeHostEvents, delay, IRpcBridgeHost} from "../Exports";

export class AutoRecoverableRpcBridgeHost implements IRpcBridgeHost<IRpcBridgeHost<any>> {
    backend: IRpcBridgeHost<any> = null!
    private closedByUser = false
    private connectionIsReestablished = false
    private _handler: BridgeHostEvents = null!

    public constructor(private bridgeCreator: () => Promise<IRpcBridgeHost<any>>, private delayMilliSecondsBeforeReconnect = 1000 * 10, private onConnectError = async () => {}, private onDisconnect = async () => {}, private onReconnect = async () => {}) {}
    public async prepare(eventHandler: BridgeHostEvents): Promise<void> {
        this.backend = await this.bridgeCreator()
        this._handler = new BridgeHostEvents(
            async () => {
                await eventHandler.onReady()
                if (this.connectionIsReestablished) await this.onReconnect()
            },
            eventHandler.onError,
            async () => {
                await eventHandler.onClose()
                await this.onDisconnect()
                if (this.closedByUser) return
                await delay(this.delayMilliSecondsBeforeReconnect)
                await this.rePrepare(eventHandler)
            },
            async () => {
                await eventHandler.onUnableToConnect()
                if (this.closedByUser) return
                await this.onConnectError()
                await delay(this.delayMilliSecondsBeforeReconnect)
                await this.rePrepare(eventHandler)
            },
            eventHandler.onMessage
        )
        await this.backend.prepare(this._handler)
    }
    private async rePrepare(eventHandler: BridgeHostEvents) {
        this.connectionIsReestablished = true
        await this.prepare(eventHandler)
        await this.open()
    }
    public async close(): Promise<void> {
        this.closedByUser = true
        await this.backend.close()
    }
    public async open(): Promise<void> {
        try {
            await this.backend.open()
        }
        catch (error) {
            await this._handler.onUnableToConnect()
            await this.close()
        }
    }
    public async send(data: Uint8Array): Promise<void> {
        await this.backend.send(data)
    }
}
